package com.hyt.it.ogt.kq.service.gov.examsystem.implement;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.hyt.common.apiInfor.ApiResponse;
import com.hyt.common.utils.result.Result;
import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.kq.common.config.ConfigManager;
import com.hyt.it.ogt.kq.common.gov.config.HuaweiCloudObsConfig;
import com.hyt.it.ogt.kq.common.gov.constant.CodeEnum;
import com.hyt.it.ogt.kq.common.gov.constant.ResponseCode;
import com.hyt.it.ogt.kq.common.gov.constant.StringCodeEnum;
import com.hyt.it.ogt.kq.common.gov.constant.examsys.ActionEnum;
import com.hyt.it.ogt.kq.common.gov.constant.examsys.gce.*;
import com.hyt.it.ogt.kq.common.gov.enums.TaskConfigCodeEnum;
import com.hyt.it.ogt.kq.common.config.exception.KqException;
import com.hyt.it.ogt.kq.common.gov.secmodule.CScModuleUtil;
import com.hyt.it.ogt.kq.common.gov.secmodule.SecModuleUtil;
import com.hyt.it.ogt.kq.common.gov.utils.DateTimeUtil;
import com.hyt.it.ogt.kq.common.gov.utils.FileUtil;
import com.hyt.it.ogt.kq.common.gov.utils.HuaweiCloudObsUtil;
import com.hyt.it.ogt.kq.common.gov.utils.XmlUtil;
import com.hyt.it.ogt.kq.service.gov.examsystem.ExamSystem;
import com.hyt.it.ogt.kq.service.gov.feign.tps.GovTpsClient;
import com.hyt.it.ogt.kq.service.gov.mapper.BackUpMapper;
import com.hyt.it.ogt.kq.service.gov.mapper.BehaviorAnalysisMapper;
import com.hyt.it.ogt.kq.service.gov.model.dto.CandidateInfoGceResultDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.PaperFileDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.PlaceRoomInfoDTO;
import com.hyt.it.ogt.kq.service.gov.model.dto.RoomDataPackageDTO;
import com.hyt.it.ogt.kq.service.gov.model.entity.*;
import com.hyt.it.ogt.kq.service.gov.model.json.examsys.HuaweiCloudObsInfo;
import com.hyt.it.ogt.kq.service.gov.model.json.examsys.gce.UploadResponse;
import com.hyt.it.ogt.kq.service.gov.model.param.OumaParam;
import com.hyt.it.ogt.kq.service.gov.model.vo.BehaviorAnalysisInfoVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.BehaviorAnalysisVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.BehaviorResultVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.BehaviorAnalysisInfoVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.BehaviorAnalysisVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.BehaviorResultVo;
import com.hyt.it.ogt.kq.service.gov.model.vo.TaskTimeVO;
import com.hyt.it.ogt.kq.service.gov.model.vo.obs.HuaweiObsObjectMetaDataVO;
import com.hyt.it.ogt.kq.service.gov.model.xml.examsys.gce.*;
import com.hyt.it.ogt.kq.service.gov.photo.PhotoTypeEnum;
import com.hyt.it.ogt.kq.service.gov.roomdata.model.obt.ExamTime;
import com.hyt.it.ogt.kq.service.gov.service.*;
import com.hyt.it.ogt.kq.service.gov.service.obs.CloudObsService;
import com.hyt.it.ogt.kq.service.model.entity.Subject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.nio.charset.Charset;
/**
 * <p>
 * GCE考试系统接口实现
 * </p>
 *
 * @author huangyh
 * @since 2021-07-19
 */
@Slf4j
@Component
public class GceImplement implements ExamSystem {

    @Resource
    private ITaskService iTaskService;

    @Resource
    private ITaskConfigService iTaskConfigService;

    @Resource
    private ConfigManager configManager;

    @Resource
    CloudObsService cloudObsService;

    @Resource
    private ITaskRoomService iTaskRoomService;

    @Resource
    private IRegisterInfoService iRegisterInfoService;

    @Resource
    private HuaweiCloudObsConfig huaweiCloudObsConfig;

    @Resource
    private ITimeRoomService iTimeRoomService;

    @Resource
    private IExamMonitorService iExamMonitorService;

    @Resource
    private IExamResultMonitorService iExamResultMonitorService;

    @Resource
    private ISysFileService iSysFileService;

    @Resource
    private IPaperService iPaperService;

    @Resource
    private ICandidateService iCandidateService;

    @Resource
    private IMonitorSettingBackupService iMonitorSettingBackupService;

    @Resource
    private IExamPackageService iExamPackageService;

    @Resource
    private ITaskPlaceService iTaskPlaceService;

    @Resource
    private ITaskRoomTestReportService iTaskRoomTestReportService;

    @Resource
    private ITaskRoomTestReportDetailService iTaskRoomTestReportDetailService;

    @Resource
    private ITaskTestInventoryService iTaskTestInventoryService;

    @Resource
    private GovTpsClient tpsClient;

    @Resource
    private ITimeService iTimeService;

    @Resource
    IBehaviorAnalysisService iBehaviorAnalysisService;
    @Resource
    BehaviorAnalysisMapper mapper;

    @Resource
    BackUpMapper backUpMapper;

    @Override
    public String getRegisterInfo(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String code = request.getParameter(GCEParamEnum.GET_REGISTER_INFO_CODE.getParam());
        //获取考场注册信息
        RegisterInfoGceResult registerInfoGceResult = iTaskRoomService.getGceRegisterRoomInfo(taskCode, code);
        if (ObjectUtils.isEmpty(registerInfoGceResult)) {
            //未找到考场信息
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "未找到考场信息");
            registerInfoGceResult = new RegisterInfoGceResult();
            registerInfoGceResult.setFlag(GCEResultEnum.GET_REGISTER_INFO_NOT_FOUND.getResult());
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    XmlUtil.toXml(registerInfoGceResult));
        }
        //找到考场信息
        registerInfoGceResult.setFlag(GCEResultEnum.GET_REGISTER_INFO_FOUND.getResult());
        return XmlUtil.toXml(registerInfoGceResult);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String newRegisterRoom(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String roomCode = request.getParameter(GCEParamEnum.NEW_REGISTER_ROOM_ROOM_CODE.getParam());
        //根据考试任务编号获取考场注册信息
        TaskRoom taskRoom = iTaskRoomService.getByTaskCodeAndCode(taskCode, roomCode);
        //校验考场注册信息是否为空
        if (ObjectUtils.isEmpty(taskRoom)) {
            //未找到相应考场，注册失败
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    "未找到相应考场，注册失败");
            throw new KqException(GCEResultEnum.NEW_REGISTER_ROOM_NOT_FOUND.getResult(),
                    GCEResultEnum.NEW_REGISTER_ROOM_NOT_FOUND.getRemark());
        }
        //校验考试系统版本号
        String examSystemVersion = request.getParameter(GCEParamEnum.NEW_REGISTER_ROOM_VERSION.getParam());
        if (!this.verifyExamSystemVersion(taskCode, examSystemVersion)) {
            //考试系统软件版本不符合要求
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    "考试系统软件版本不符合要求");
            throw new KqException(GCEResultEnum.NEW_REGISTER_ROOM_VERSION_TOO_LOW.getResult(),
                    GCEResultEnum.NEW_REGISTER_ROOM_VERSION_TOO_LOW.getRemark());
        }
        //保存考场注册信息
        if (!this.saveRegisterInfo(taskCode, taskRoom.getId(), request)) {
            //插入或更新注册信息表失败
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    "插入或更新注册信息表失败");
            throw new KqException(GCEResultEnum.NEW_REGISTER_ROOM_EXCEPTION.getResult(),
                    GCEResultEnum.NEW_REGISTER_ROOM_EXCEPTION.getRemark());
        }
        //更新考场注册状态
        taskRoom.setRegistered(true);
        if (!iTaskRoomService.updateById(taskRoom)) {
            //更新任务考场表失败
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    "更新任务考场表失败");
            throw new KqException(GCEResultEnum.NEW_REGISTER_ROOM_EXCEPTION.getResult(),
                    GCEResultEnum.NEW_REGISTER_ROOM_EXCEPTION.getRemark());
        }
        //考场注册成功
        return String.valueOf(GCEResultEnum.NEW_REGISTER_ROOM_SUCCESS.getResult());
    }

    /**
     * 校验考试系统版本号是否符合要求
     *
     * @param taskCode          考试任务编号
     * @param examSystemVersion 考试系统版本号
     * @return true：符合要求，false：不符合要求
     */
    private boolean verifyExamSystemVersion(String taskCode, String examSystemVersion) {
        //获取考试系统版本号
        Optional<String> optional = Optional.ofNullable(iTaskConfigService.getValueByTaskCode(taskCode,
                TaskConfigCodeEnum.EXAM_VERSION.getCode()));
        return optional.map(examinationExamSystemVersion -> {
            List<String> versionList = Arrays.stream(examinationExamSystemVersion.split("\\|"))
                    .map(String::trim)
                    .collect(Collectors.toList());
            return versionList.contains(examSystemVersion);
        }).orElse(false);
    }

    /**
     * 保存考场注册信息
     *
     * @param taskCode 考试任务编号
     * @param roomId   任务考场id
     * @param request  考试系统请求参数
     */
    private boolean saveRegisterInfo(String taskCode, String roomId, HttpServletRequest request) {
        //校验考场是否已被注册
        boolean roomRegistered = iRegisterInfoService.roomRegistered(roomId);
        if (roomRegistered) {
            //当前考场已被注册
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    "当前考场已被注册");
            throw new KqException(GCEResultEnum.NEW_REGISTER_ROOM_REGISTERED_BY_ROOM.getResult(),
                    GCEResultEnum.NEW_REGISTER_ROOM_REGISTERED_BY_ROOM.getRemark());
        }
        //校验当前监考机是否已注册了其他考场
        Boolean monitorRegistered = iRegisterInfoService.monitorRegistered(taskCode,
                request.getParameter(GCEParamEnum.NEW_REGISTER_ROOM_MAC.getParam()));
        if (monitorRegistered) {
            //当前计算机已经注册了其他考场
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    "当前计算机已经注册了其他考场");
            throw new KqException(GCEResultEnum.NEW_REGISTER_ROOM_REGISTERED.getResult(),
                    GCEResultEnum.NEW_REGISTER_ROOM_REGISTERED.getRemark());
        }
        //当前没有注册信息，插入注册信息
        RegisterInfo newRegisterInfo = new RegisterInfo();
        newRegisterInfo.setRoomId(roomId)
                .setLinkman(request.getParameter(GCEParamEnum.NEW_REGISTER_ROOM_LINK_MAN.getParam()))
                .setTelephone(request.getParameter(GCEParamEnum.NEW_REGISTER_ROOM_TEL.getParam()))
                .setIp(request.getParameter(GCEParamEnum.NEW_REGISTER_ROOM_IP_ADDRESS.getParam()))
                .setMac(request.getParameter(GCEParamEnum.NEW_REGISTER_ROOM_MAC.getParam()))
                .setCpuDiskCode(request.getParameter(GCEParamEnum.NEW_REGISTER_ROOM_CPU_AND_DISK_NO.getParam()));
        return iRegisterInfoService.add(newRegisterInfo);
    }

    @Override
    public String getExamTaskList(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        ExamTaskGceResult examTaskGceResult = new ExamTaskGceResult();
        //获取考试任务信息
        Task task = iTaskService.getTaskByCode(taskCode);
        if (ObjectUtils.isEmpty(task)) {
            //考试任务已不存在，获取失败
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "考试任务已不存在，获取失败");
            examTaskGceResult.setFlag(GCEResultEnum.GET_EXAM_TASK_LIST_FAILED.getResult());
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    XmlUtil.toXml(examTaskGceResult));
        }
        //获取考试任务对应的考试系统配置
        List<TaskConfig> taskConfigList = iTaskConfigService.listByTaskCode(taskCode);
        //组装返回结果数据
        ExamTaskInfoGceResult info = new ExamTaskInfoGceResult();
        info.setName(task.getName()).setCode(task.getCode())
                .setYear(String.valueOf(task.getBeginDate().getYear()))
                .setMonth(String.valueOf(task.getBeginDate().getMonthValue()))
                .setEffectiveDate(DateTimeUtil.getDateTimeString(task.getEndDate()))
                .setMainUrl(configManager.getExaminationUrl() + taskCode)
                .setBackUrl(configManager.getExaminationUrl() + taskCode)
                .setTimeSynchronizationServices(configManager.getExaminationUrl())
                .setExamType(GCEExamTypeEnum.STRICT_MODE.getType())
                //获取考试任务对应的考试系统版本号
                .setExamSystemVersion(iTaskConfigService.getValueByCode(taskConfigList,
                        TaskConfigCodeEnum.EXAM_VERSION.getCode()))
                //获取ExamClassCode
                .setExamClassCode(iTaskConfigService.getValueByCode(taskConfigList,
                        TaskConfigCodeEnum.EXAM_CLASS_CODE.getCode()))
                //获取考试系统脚本号
                .setScriptVersion(iTaskConfigService.getValueByCode(taskConfigList,
                        TaskConfigCodeEnum.SCRIPT_VERSION.getCode()))
                //获取严格对号入座模式
                .setSeatMode(iTaskConfigService.getValueByCode(taskConfigList,
                        TaskConfigCodeEnum.SEAT_NO.getCode()))
                .setProjectType(String.valueOf(task.getType()))
                .setExaminationType(String.valueOf(CodeEnum.GCE_EXAMINATION_TYPE.getCode()));
        List<ExamTaskInfoGceResult> examTaskInfoGceResultList = new ArrayList<>();
        examTaskInfoGceResultList.add(info);
        examTaskGceResult.setItemList(examTaskInfoGceResultList)
                .setDatetime(String.valueOf(DateTimeUtil.date2double(Calendar.getInstance().getTime())))
                .setFlag(GCEResultEnum.GET_EXAM_TASK_LIST_SUCCESS.getResult());
        return XmlUtil.toXml(examTaskGceResult);
    }

    @Override
    public String getRoomPackage(HttpServletRequest request, HttpServletResponse response, String taskCode) throws Exception {
        //成功返回数据示例（不含双引号，多个批次用“|”分割）:"0#考试时间场次ID#考试场次名称#考试场次开考时间#考试时长#考试科目名称#考场考务场次数据包状态#包的MD5值#包名的HTTP地址"
        String roomCode = request.getParameter(GCEParamEnum.GET_ROOM_PACKAGE_ROOM_CODE.getParam());
        //获取考务包信息，GCE考试系统无论是正式考试还是模拟测试，均提供正式考务包即可，无需区分正式考试和模拟测试
        List<RoomDataPackageDTO> roomDataPackageDTOList = iTimeRoomService.getRoomDataPackageDTOList(taskCode, roomCode);
        //生成考务包信息的文本
        StringBuffer result = new StringBuffer();
        //组装考务包信息数据
        this.convertRoomPackageInfo(request, roomDataPackageDTOList, result);

        //String unicode = new String(result.toString().getBytes(),"UTF-8");
      //  String gbkStr = new String(unicode.getBytes("GB2312"));
//        byte[] bytes = result.toString().getBytes(Charset.forName("GB18030"));
        String encrypString = SecModuleUtil.encrypString(result.toString());

        String decrypString = SecModuleUtil.encrypStringNew(encrypString);
//        byte[] str = decrypString.substring(decrypString.length()-10).getBytes();
        log.info("考务包数据解密：{}",decrypString);
        log.info("考务包数据长度：{}",result.toString().length());
        log.info("考务包数据加密长度：{}",encrypString.length());
        //加密返回结果
        return encrypString;
    }

    /**
     * 组装考务包信息数据
     * @param request 请求
     * @param roomDataPackageDTOList 考务包信息
     * @param result 生成考务包信息的文本
     */
    private void convertRoomPackageInfo(HttpServletRequest request, List<RoomDataPackageDTO> roomDataPackageDTOList,
                                        StringBuffer result) {
        //判断是否存在考务包
        if (CollectionUtils.isEmpty(roomDataPackageDTOList)) {
            //未有考务包
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    GCEResultEnum.GET_ROOM_PACKAGE_WITHOUT.getRemark());
            throw new KqException(GCEResultEnum.GET_ROOM_PACKAGE_WITHOUT.getResult(),
                    GCEResultEnum.GET_ROOM_PACKAGE_WITHOUT.getRemark());
        }
        result.append(GCEResultEnum.GET_ROOM_PACKAGE_SUCCESS.getResult())
                .append(ActionEnum.POUND_SIGN.getName());
        roomDataPackageDTOList.forEach(roomDataPackageDTO -> {
            //计算考试时间
            Duration duration = Duration.between(roomDataPackageDTO.getStartTime(),
                    roomDataPackageDTO.getEndTime());
            long examTime = duration.toMinutes();
            //组装科目名称
            String subjectNames = roomDataPackageDTO.getSubjectList().stream()
                    .map(Subject::getName)
                    .collect(Collectors.joining(","));
            //考试时间场次id
            result.append(roomDataPackageDTO.getCode())
                    .append(ActionEnum.POUND_SIGN.getName())
                    //考试场次名称
                    .append(roomDataPackageDTO.getName())
                    .append(ActionEnum.POUND_SIGN.getName())
                    //考试场次开考时间
                    .append(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
                            .format(roomDataPackageDTO.getStartTime()))
                    .append(ActionEnum.POUND_SIGN.getName())
                    //考试时长
                    .append(examTime)
                    .append(ActionEnum.POUND_SIGN.getName())
                    //考试科目名称
                    .append(subjectNames)
                    .append(ActionEnum.POUND_SIGN.getName())
                    //数据包状态
                    .append(GCEResultEnum.ROOM_PACKAGE_USABLE.getResult())
                    .append(ActionEnum.POUND_SIGN.getName())
                    //MD5
                    .append(roomDataPackageDTO.getRoomDataMd5())
                    .append(ActionEnum.POUND_SIGN.getName())
                    //路径
                    .append(roomDataPackageDTO.getRoomDataUrl())
                    .append(ActionEnum.VERTICAL_SIGN.getName());
        });
        //去除最后一个分隔符
        result.deleteCharAt(result.length() - 1);
    }

    @Override
    public String updateRoomPackageState(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //暂无考务包下载管理表，直接返回成功状态
        return String.valueOf(GCEResultEnum.UPDATE_ROOM_PACKAGE_STATE_SUCCESS.getResult());
    }

    @Override
    public String getPaperData(HttpServletRequest request, HttpServletResponse response, String taskCode){
        //获取请求参数
        String dataType = request.getParameter(GCEParamEnum.GET_PAPER_DATA_TYPE.getParam());
        String roomCode = request.getParameter(GCEParamEnum.GET_PAPER_DATA_ROOM_CODE.getParam());
        String timeCode = request.getParameter(GCEParamEnum.GET_PAPER_DATA_TIME_CODE.getParam());
        //试卷数据返回结果
        StringBuffer result = new StringBuffer();
        //试卷文件信息DTO
        List<PaperFileDTO> paperList;
        if (GCEParamEnum.GET_PAPER_DATA_TYPE_FORMAL.getParam().equals(dataType)) {
            //正式试卷包
            paperList = iPaperService.getForExamSystem(taskCode, roomCode,
                    timeCode, CodeEnum.FORMAL_PAPER.getCode());
        } else if (GCEParamEnum.GET_PAPER_DATA_TYPE_MOCK.getParam().equals(dataType)) {
            //模拟试卷包
            paperList = iPaperService.getForExamSystem(taskCode, roomCode,
                    timeCode, CodeEnum.MOCK_PAPER.getCode());
        } else {
            //试卷包类型不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName())
                    + ": {}", "试卷包类型不存在");
            throw new KqException(GCEResultEnum.GET_PAPER_DATA_WITHOUT.getResult(),
                    GCEResultEnum.GET_PAPER_DATA_WITHOUT.getRemark());
        }
        //组装试卷数据返回结果
        this.convertPaperData(request, paperList, result);
        //加密返回结果
        return SecModuleUtil.encrypStringNew(result.toString());
    }

    /**
     * 组装试卷数据返回结果
     * @param request 请求
     * @param paperList 试卷文件信息DTO
     * @param result 试卷数据返回结果
     */
    private void convertPaperData(HttpServletRequest request, List<PaperFileDTO> paperList, StringBuffer result) {
        if (CollectionUtils.isEmpty(paperList)) {
            //未有试卷包，获取失败
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    GCEResultEnum.GET_PAPER_DATA_WITHOUT.getRemark());
            throw new KqException(GCEResultEnum.GET_PAPER_DATA_WITHOUT.getResult(),
                    GCEResultEnum.GET_PAPER_DATA_WITHOUT.getRemark());
        }
        //组装试卷信息返回数据
        result.append(GCEResultEnum.GET_PAPER_DATA_SUCCESS.getResult())
                .append(ActionEnum.VERTICAL_SIGN.getName());
        paperList.forEach(paper -> {
            //试卷MD5
            result.append(paper.getMd5().toLowerCase())
                    //井号分隔符
                    .append(ActionEnum.POUND_SIGN.getName())
                    //试卷路径
                    .append(paper.getUrl())
                    //竖线分隔符
                    .append(ActionEnum.VERTICAL_SIGN.getName());
        });
        //去除最后一个分隔符
        result.deleteCharAt(result.length() - 1);
    }

    @Override
    public String updatePaperDataState(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //暂无试卷包下载管理表，直接返回成功状态
        return String.valueOf(GCEResultEnum.UPDATE_PAPER_DATA_STATE_SUCCESS.getResult());
    }

    @Override
    public String getPaperAuthorizationCode(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String type = request.getParameter(GCEParamEnum.GET_PAPER_AUTHORIZATION_CODE_TYPE.getParam());
        String timeCode = request.getParameter(GCEParamEnum.GET_PAPER_AUTHORIZATION_CODE_TIME_CODE.getParam());
        String roomCode = request.getParameter(GCEParamEnum.GET_PAPER_AUTHORIZATION_CODE_ROOM_CODE.getParam());
        String paperCode = request.getParameter(GCEParamEnum.GET_PAPER_AUTHORIZATION_CODE_PAPER_CODE.getParam());
        if (StringUtils.isEmpty(paperCode)) {
            //试卷编号不能为空
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "试卷编号不能为空");
            throw new KqException(GCEResultEnum.GET_PAPER_AUTHORIZATION_CODE_WITHOUT.getResult(),
                    GCEResultEnum.GET_PAPER_AUTHORIZATION_CODE_WITHOUT.getRemark());
        }
        //解析试卷编号
        paperCode = paperCode.replaceAll("\\|",
                ActionEnum.COMMA_SIGN.getName());
        List<String> paperCodeList = Arrays.stream(paperCode.split(ActionEnum.COMMA_SIGN.getName()))
                .map(String::trim).collect(Collectors.toList());
        //试卷授权码信息
        List<PaperFileDTO> authorizationCodeList;
        StringBuffer result = new StringBuffer();
        if (GCEParamEnum.GET_PAPER_AUTHORIZATION_CODE_TYPE_FORMAL.getParam().equals(type)) {
            //正式授权码包
            authorizationCodeList = iPaperService.getPaperAuthorizationCode(taskCode, roomCode, timeCode,
                    CodeEnum.FORMAL_PAPER.getCode(), paperCode);
            //组装返回结果数据
            this.convertPaperAuthorizationCodeInfo(request, result, paperCodeList, authorizationCodeList);
        } else if (GCEParamEnum.GET_PAPER_AUTHORIZATION_CODE_TYPE_MOCK.getParam().equals(type)) {
            //模拟授权码包
            authorizationCodeList = iPaperService.getPaperAuthorizationCode(taskCode, roomCode, timeCode,
                    CodeEnum.MOCK_PAPER.getCode(), paperCode);
            //组装返回结果数据
            this.convertPaperAuthorizationCodeInfo(request, result, paperCodeList, authorizationCodeList);
        } else {
            //包类型不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "包类型不存在");
            throw new KqException(GCEResultEnum.GET_PAPER_AUTHORIZATION_CODE_WITHOUT.getResult(),
                    GCEResultEnum.GET_PAPER_AUTHORIZATION_CODE_WITHOUT.getRemark());
        }
        //去除最后一个分隔符
        result.deleteCharAt(result.length() - 1);
        //加密返回结果
        return SecModuleUtil.encrypStringNew(result.toString());
    }

    /**
     * 组装授权码数据返回结果
     * @param request 请求
     * @param result 返回结果
     * @param paperCodeList 试卷编号
     * @param authorizationCodeList 授权码
     */
    private void convertPaperAuthorizationCodeInfo(HttpServletRequest request, StringBuffer result,
                                                   List<String> paperCodeList, List<PaperFileDTO> authorizationCodeList) {
        //校验试卷授权码是否存在
        if (CollectionUtils.isEmpty(authorizationCodeList)) {
            //未有试卷授权码，获取失败
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    GCEResultEnum.GET_PAPER_AUTHORIZATION_CODE_WITHOUT.getRemark());
            throw new KqException(GCEResultEnum.GET_PAPER_AUTHORIZATION_CODE_WITHOUT.getResult(),
                    GCEResultEnum.GET_PAPER_AUTHORIZATION_CODE_WITHOUT.getRemark());
        }
        result.append(GCEResultEnum.GET_PAPER_AUTHORIZATION_CODE_SUCCESS.getResult())
                //竖线分隔符
                .append(ActionEnum.VERTICAL_SIGN.getName());
        paperCodeList.forEach(code -> {
            //试卷编号，井号分隔符
            result.append(code).append(ActionEnum.POUND_SIGN.getName());
            Optional<PaperFileDTO> optional = authorizationCodeList.stream()
                    .filter(authorizationCode -> authorizationCode.getCode().equals(code))
                    .findFirst();
            if (optional.isPresent()) {
                //存在试卷，返回全路径
                result.append(optional.get().getUrl());
            } else {
                //不存在试卷，返回指定数字
                result.append(GCEResultEnum.GET_PAPER_AUTHORIZATION_CODE_PARTIAL_DELETED.getResult());
            }
            //竖线分隔符
            result.append(ActionEnum.VERTICAL_SIGN.getName());
        });
    }

    @Override
    public String updatePaperAuthorizationCodeState(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //暂无授权码下载管理表，直接返回成功状态
        return String.valueOf(GCEResultEnum.UPDATE_PAPER_AUTHORIZATION_CODE_STATE_SUCCESS.getResult());
    }

    @Override
    public String uploadTestReport(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String roomCode = request.getParameter(GCEParamEnum.UPLOAD_TEST_REPORT_ROOM_CODE.getParam());
        String reportXml = request.getParameter(GCEParamEnum.UPLOAD_TEST_REPORT_RESULT.getParam());
        //获取任务考点考场信息
        PlaceRoomInfoDTO placeRoomInfoDTO = iTaskPlaceService.getPlaceRoomInfoByCode(taskCode, roomCode);
        //校验任务考点考场信息
        if (ObjectUtils.isEmpty(placeRoomInfoDTO)) {
            //任务考点考场信息不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "任务考点考场信息不存在");
            throw new KqException(GCEResultEnum.UPLOAD_TEST_REPORT_FAILED.getResult(),
                    GCEResultEnum.UPLOAD_TEST_REPORT_FAILED.getRemark());
        }
        //获取考场机位信息
        Integer seatNumber = iTaskRoomService.getSeatNumberByCode(taskCode, roomCode);
        if (ObjectUtils.isEmpty(seatNumber)) {
            //考场机位信息不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "考场机位信息不存在");
            throw new KqException(GCEResultEnum.UPLOAD_TEST_REPORT_FAILED.getResult(),
                    GCEResultEnum.UPLOAD_TEST_REPORT_FAILED.getRemark());
        }
        //转换测试报告XML文本
        GceTestReport gceTestReport = XmlUtil.toObject(reportXml, GceTestReport.class);
        //设置测试报告数据
        TaskRoomTestReport taskRoomTestReport = new TaskRoomTestReport();
        //设置考点考场信息
        BeanUtils.copyProperties(placeRoomInfoDTO, taskRoomTestReport);
        //设置监考机信息
        GceMonitorReport gceMonitorReport = gceTestReport.getGceMonitorReport();
        BeanUtils.copyProperties(gceMonitorReport, taskRoomTestReport);
        //判断监考机测试结果
        taskRoomTestReport.setMonitorPass(gceMonitorReport.getMonitorPass() == 1);
        //判断考生机测试结果
        GceTestInfoReport gceTestInfoReport = gceTestReport.getGceTestInfoReport();
        Integer resultCount = Integer.valueOf(gceTestInfoReport.getResultCount());
        taskRoomTestReport.setClientPass(resultCount.compareTo(seatNumber) >= 0);
        //设置环境测试结果
        taskRoomTestReport.setClientTestNumber(Integer.valueOf(gceTestInfoReport.getClientCount()))
                .setClientPassNumber(resultCount);
        //保存考场测试报告
        this.saveTestReport(taskRoomTestReport, reportXml, request);
        return String.valueOf(GCEResultEnum.UPLOAD_TEST_REPORT_SUCCESS.getResult());
    }

    /**
     * 保存测试报告
     * @param taskRoomTestReport 测试报告
     * @param reportXml 测试报告详情XML
     * @param request 请求
     * @throws KqException 异常
     */
    private void saveTestReport(TaskRoomTestReport taskRoomTestReport,
                                String reportXml, HttpServletRequest request) {
        //获取考务中的测试报告
        TaskRoomTestReport testReport = iTaskRoomTestReportService
                .getByTaskRoom(taskRoomTestReport.getTaskId(), taskRoomTestReport.getRoomId());
        if (ObjectUtils.isEmpty(testReport)) {
            //不存在测试报告，插入测试报告
            if (!iTaskRoomTestReportService.save(taskRoomTestReport)) {
                //插入测试报告表失败
                log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "插入测试报告表失败");
                throw new KqException(GCEResultEnum.UPLOAD_TEST_REPORT_FAILED.getResult(),
                        GCEResultEnum.UPLOAD_TEST_REPORT_FAILED.getRemark());
            }
        } else {
            //存在测试报告，更新测试报告
            taskRoomTestReport.setId(testReport.getId());
            BeanUtils.copyProperties(taskRoomTestReport, testReport);
            iTaskRoomTestReportService.updateById(testReport);
        }
        //获取考务中的测试报告详情
        TaskRoomTestReportDetail taskRoomTestReportDetail = iTaskRoomTestReportDetailService
                .getByReportId(taskRoomTestReport.getId());
        if (ObjectUtils.isEmpty(taskRoomTestReportDetail)) {
            //不存在测试报告详情，插入测试报告详情
            TaskRoomTestReportDetail detail = new TaskRoomTestReportDetail();
            detail.setContent(reportXml)
                    .setTestReportId(taskRoomTestReport.getId());
            if (!iTaskRoomTestReportDetailService.save(detail)) {
                //插入测试报告详情表失败
                log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "插入测试报告详情表失败");
                throw new KqException(GCEResultEnum.UPLOAD_TEST_REPORT_FAILED.getResult(),
                        GCEResultEnum.UPLOAD_TEST_REPORT_FAILED.getRemark());
            }
            return;
        }
        //存在测试报告详情，更新测试报告详情
        taskRoomTestReportDetail.setContent(reportXml);
        iTaskRoomTestReportDetailService.updateById(taskRoomTestReportDetail);
    }

    @Override
    public String examPackageSync(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String syncInfo = request.getParameter(GCEParamEnum.SYNC_EXAM_PACKAGE_SYNC_INFO.getParam());
        //请求参数转换为对象
        ExamPackageSyncInfo examPackageSyncInfo = XmlUtil.toObject(syncInfo, ExamPackageSyncInfo.class);
        //根据考试任务编号获取基础包版本信息
        BaseDataPackage baseDataPackage = iExamPackageService.getBaseDataPackageByTaskCode(examPackageSyncInfo.getTaskCode());
        //根据考试任务编号、考场编号获取考务包版本信息数据
        List<RoomDataPackageDTO> roomDataPackageDTOList = iTimeRoomService
                .getRoomDataPackageDTOList(examPackageSyncInfo.getTaskCode(), examPackageSyncInfo.getRoomCode());
        //创建同步考试数据包结果数据
        ExamPackageSyncResult result = new ExamPackageSyncResult();
        result.setTaskCode(examPackageSyncInfo.getTaskCode())
                .setTimeCount(roomDataPackageDTOList.size());
        //比较并判断基础包版本是否已变更
        int baseDataPackageChanged = Optional.ofNullable(baseDataPackage)
                .filter(baseData -> baseData.getVersion().equals(examPackageSyncInfo.getBaseDataPackageVersion()))
                .map(baseData -> GCEResultEnum.SYNC_EXAM_PACKAGE_BASE_DATA_PACKAGE_NOT_CHANGE.getResult())
                .orElse(GCEResultEnum.SYNC_EXAM_PACKAGE_BASE_DATA_PACKAGE_CHANGED.getResult());
        result.setBaseDataPackageChanged(baseDataPackageChanged);
        //比较各批次考务包版本
        List<ExamPackageSyncTimeInfo> ExamPackageSyncTimeInfoList = examPackageSyncInfo.getExamPackageSyncTimeInfoList().stream()
                .filter(timeInfo -> roomDataPackageDTOList.stream()
                        .filter(roomDataPackageDTO -> timeInfo.getTimeCode().equals(String.valueOf(roomDataPackageDTO.getCode())))
                        .findFirst()
                        .map(roomDataPackageDTO -> !timeInfo.getRoomDataPackageVersion().equals(roomDataPackageDTO.getVersion()))
                        .orElse(true)).collect(Collectors.toList());
        //将考务包版本不同的批次编号转换为考试系统的数据格式
        String changedTimeCodes = ExamPackageSyncTimeInfoList.stream()
                .map(ExamPackageSyncTimeInfo::getTimeCode)
                .collect(Collectors.joining(ActionEnum.VERTICAL_SIGN.getName()));
        result.setChangedTimeCodes(StringUtils.isNotBlank(changedTimeCodes) ? changedTimeCodes : StringUtils.EMPTY);
        //返回同步考试数据包结果
        return XmlUtil.toXmlWithNoneEscape(result);
    }

    @Override
    public String updateClosure(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String roomCode = request.getParameter(GCEParamEnum.UPDATE_CLOSURE_ROOM_CODES.getParam());
        if (StringUtils.isBlank(roomCode)) {
            //考场编号为空
            log.error(request.getParameter(ActionEnum.ACTION.getName())
                    + ": {}", "考场编号为空");
            throw new KqException(GCEResultEnum.UPDATE_CLOSURE_FAILED.getResult(),
                    GCEResultEnum.UPDATE_CLOSURE_FAILED.getRemark());
        }
        //更新封场状态
        iTaskRoomService.updateClosureByCode(taskCode, roomCode);
        return String.valueOf(GCEResultEnum.UPDATE_CLOSURE_SUCCESS.getResult());
    }

    @Override
    public String getMonitorSettingBackupPath(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取考务地址
        String examinationUrl = configManager.getExaminationUrl();
        //获取请求参数
        String roomCode = request.getParameter(GCEParamEnum.GET_MONITOR_SETTING_BACKUP_PATH_ROOM_CODE.getParam());
        //校验考务地址是否存在
        if (StringUtils.isBlank(examinationUrl)) {
            //考务地址不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "考务地址不存在");
            throw new KqException(GCEResultEnum.GET_MONITOR_SETTING_BACKUP_PATH_FAILED.getResult(),
                    GCEResultEnum.GET_MONITOR_SETTING_BACKUP_PATH_FAILED.getRemark());
        }
        //校验考场是否存在
        if (!iTaskRoomService.existByTaskCodeAndCode(taskCode, roomCode)) {
            //考务地址不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "考场不存在");
            throw new KqException(GCEResultEnum.GET_MONITOR_SETTING_BACKUP_PATH_FAILED.getResult(),
                    GCEResultEnum.GET_MONITOR_SETTING_BACKUP_PATH_FAILED.getRemark());
        }
        //组装返回结果
        StringBuffer result = new StringBuffer();
        result.append(GCEResultEnum.GET_MONITOR_SETTING_BACKUP_PATH_SUCCESS.getResult())
                //竖线分隔符
                .append(ActionEnum.VERTICAL_SIGN.getName())
                //考务地址
                .append(examinationUrl)
                //考试任务编号
                .append(taskCode)
                //斜杠
                .append(GCEUploadConstant.DIRECTORY_SEPARATOR)
                //GCE考试系统上传文件接口名称
                .append(GCEUploadConstant.INTERFACE_NAME)
                //斜杠
                .append(GCEUploadConstant.DIRECTORY_SEPARATOR)
                //上传文件类型：监考端配置备份信息
                .append(GCEUploadConstant.UPLOAD_TYPE_MONITOR_BACKUP)
                //斜杠
                .append(GCEUploadConstant.DIRECTORY_SEPARATOR)
                //考场编号
                .append(roomCode);
        return result.toString();
    }

    @Override
    public String updateMonitorSettingBackupState(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //暂无上传监考端配置备份信息状态监控表，直接返回成功
        return String.valueOf(GCEResultEnum.UPDATE_MONITOR_SETTING_BACKUP_STATE_SUCCESS.getResult());
    }

    @Override
    public String getMonitorSettingBackup(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String roomCode = request.getParameter(GCEParamEnum.GET_MONITOR_SETTING_ROOM_CODE.getParam());
        //根据考试任务编号、考场编号获取考场信息
        TaskRoom taskRoom = iTaskRoomService.getByTaskCodeAndCode(taskCode, roomCode);
        //获取监考端配置备份信息下载路径
        String url = Optional.ofNullable(taskRoom).map(room -> {
            //根据考场信息查询对应的监考端配置备份信息数据
            List<MonitorSettingBackup> monitorSettingBackupList = iMonitorSettingBackupService.lambdaQuery()
                    .select(MonitorSettingBackup::getTimestamp, MonitorSettingBackup::getUrl)
                    .eq(MonitorSettingBackup::getTaskId, room.getTaskId())
                    .eq(MonitorSettingBackup::getRoomId, room.getId())
                    .eq(MonitorSettingBackup::getDelFlag, false)
                    .list();
            if (CollectionUtils.isEmpty(monitorSettingBackupList)) {
                //考场还未备份过，不能下载
                log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                        GCEResultEnum.GET_MONITOR_SETTING_BACKUP_NOT_FOUND.getRemark());
                throw new KqException(GCEResultEnum.GET_MONITOR_SETTING_BACKUP_NOT_FOUND.getResult(),
                        GCEResultEnum.GET_MONITOR_SETTING_BACKUP_NOT_FOUND.getRemark());
            }
            //获取最新的监考端配置备份信息数据
            return monitorSettingBackupList.stream()
                    .max(Comparator.comparing(MonitorSettingBackup::getTimestamp))
                    .map(MonitorSettingBackup::getUrl)
                    //获取最新的监考端配置备份信息数据取值失败
                    .orElseThrow(() -> new KqException(GCEResultEnum.GET_MONITOR_SETTING_ROOM_NOT_FOUND.getResult(),
                            GCEResultEnum.GET_MONITOR_SETTING_ROOM_NOT_FOUND.getRemark()));
        }).orElseThrow(() -> new KqException(GCEResultEnum.GET_MONITOR_SETTING_ROOM_NOT_FOUND.getResult(),
                GCEResultEnum.GET_MONITOR_SETTING_ROOM_NOT_FOUND.getRemark()));
        //组装返回结果
        StringBuffer result = new StringBuffer();
        result.append(GCEResultEnum.GET_MONITOR_SETTING_SUCCESS.getResult())
                //竖线分隔符
                .append(ActionEnum.VERTICAL_SIGN.getName())
                //监考端配置备份信息下载路径
                .append(url);
        return result.toString();
    }

    @Override
    public String updateMonitorSettingBackupDownloadState(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //暂无下载监考端配置备份信息状态监控表，直接返回成功
        return String.valueOf(GCEResultEnum.UPDATE_MONITOR_SETTING_BACKUP_DOWNLOAD_STATE_SUCCESS.getResult());
    }

    public String updateBackUpState(HttpServletRequest request, HttpServletResponse response, String taskCode){
        try{
            String RoomNo = request.getParameter(GCEParamEnum.BACKUP_ROOM_NO.getParam());
            String TimeID = request.getParameter(GCEParamEnum.BACKUP_TIME_ID.getParam());
            String OrderID = request.getParameter(GCEParamEnum.BACKUP_ORDER_ID.getParam());
            String State = request.getParameter(GCEParamEnum.BACKUP_STATE.getParam());
            Task task = iTaskService.getTaskByCode(taskCode);
            TaskRoom room = iTaskRoomService.getByTaskCodeAndCode(taskCode,RoomNo);
            QueryWrapper<Time> wrapper = new QueryWrapper<Time>();
            wrapper.lambda().select(Time::getId,Time::getCode,Time::getName).eq(Time::getCode,TimeID).eq(Time::getTaskId,task.getId());
            Time time = iTimeService.getOne(wrapper);
            BackUp backUp = new BackUp();
            backUp.setOrderId(OrderID);
            backUp.setRoomId(room.getId());
            backUp.setState(Integer.valueOf(State));
            backUp.setTaskId(task.getId());
            backUp.setTimeId(time.getId());
            backUp.setUpdateDate(LocalDateTime.now());
            UpdateWrapper<BackUp> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().eq(BackUp::getTimeId,time.getId())
                    .eq(BackUp::getRoomId,room.getId()).eq(BackUp::getTaskId,task.getId())
                    .eq(BackUp::getOrderId,OrderID);
            int count  = backUpMapper.update(backUp,updateWrapper);
            if(count <= 0){
                backUp.setId(UUIDUtils.newSortUUID());
                backUpMapper.insert(backUp);
            }
            return String.valueOf(GCEResultEnum.UPDATE_BACKUP_STATE_SUCCESS.getResult());
        } catch (Exception ex){
            log.error(ex.getMessage());
            return String.valueOf(GCEResultEnum.UPDATE_BACKUP_STATE_FAILED.getResult());
        }
    }

    public String postBehaviorAnalysis(HttpServletRequest request, HttpServletResponse response, String taskCode){
        try{
            String AnalysisInfo =  request.getParameter(GCEParamEnum.BEHAVIOR_INFO.getParam());
            Task task = iTaskService.getTaskByCode(taskCode);
            if(StringUtils.isNotBlank(AnalysisInfo)){
                BehaviorAnalysisVo vo = JSON.parseObject(AnalysisInfo, BehaviorAnalysisVo.class);
                if(!ObjectUtils.isEmpty(vo) && CollectionUtil.isNotEmpty(vo.getStudentItem())){
                    TaskRoom room = iTaskRoomService.getByTaskCodeAndCode(taskCode,vo.getRoomCode());
                    QueryWrapper<Time> wrapper = new QueryWrapper<Time>();
                    wrapper.lambda().select(Time::getId,Time::getCode,Time::getName).eq(Time::getCode,vo.getTimeId()).eq(Time::getTaskId,task.getId());
                    Time time = iTimeService.getOne(wrapper);
                    List<BehaviorAnalysis> result = new ArrayList<>();
                    for(int i = 0; i < vo.getStudentItem().size(); i++){
                        BehaviorAnalysisInfoVo behaviorVo =  vo.getStudentItem().get(i);
                        BehaviorAnalysis behaviorAnalysis = new BehaviorAnalysis();
                        behaviorAnalysis.setXwId(behaviorVo.getXwId());
                        behaviorAnalysis.setTaskId(task.getId());
                        behaviorAnalysis.setRoomId(room.getId());
                        behaviorAnalysis.setTimeId(time.getId());
                        behaviorAnalysis.setBehaviorCount(Integer.valueOf(behaviorVo.getCount()));
                        behaviorAnalysis.setBehaviorInfo(behaviorVo.getBehaviorInfo());
                        behaviorAnalysis.setOperateTime(LocalDateTime.parse(behaviorVo.getOperatetime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                        behaviorAnalysis.setBehaviorType(behaviorVo.getType());
                        behaviorAnalysis.setIsChecked(behaviorVo.getIsChecked());
                        behaviorAnalysis.setReplyNum(Integer.valueOf(behaviorVo.getReplyNum()));
                        behaviorAnalysis.setZkzh(behaviorVo.getZkzh());
                        UpdateWrapper<BehaviorAnalysis> updateWrapper = new UpdateWrapper<>();
                        updateWrapper.lambda().eq(BehaviorAnalysis::getTimeId,time.getId())
                                .eq(BehaviorAnalysis::getRoomId,room.getId()).eq(BehaviorAnalysis::getTaskId,task.getId())
                                .eq(BehaviorAnalysis::getXwId,behaviorVo.getXwId()).eq(BehaviorAnalysis::getZkzh,behaviorVo.getZkzh());
                        int count  = mapper.update(behaviorAnalysis,updateWrapper);
                        if(count <= 0){
                            behaviorAnalysis.setId(UUIDUtils.newSortUUID());
                            result.add(behaviorAnalysis);
                        }
                    }
                    if(CollectionUtil.isNotEmpty(result)){
                        iBehaviorAnalysisService.saveBatch(result);
                    }

                }
            }
            BehaviorResultVo result = new BehaviorResultVo();
            result.setFlag(0);
            result.setMessage("上传行为分析成功");
            return JSON.toJSONString(result);
        } catch (Exception ex){
            log.error(ex.getMessage());
            BehaviorResultVo result = new BehaviorResultVo();
            result.setFlag(1);
            result.setMessage("上传行为分析失败");
            return JSON.toJSONString(result);
        }
    }

    @Override
    public String getBasePackage(HttpServletRequest request, HttpServletResponse response, String taskCode){
        //获取基础包信息，GCE考试系统无需区分正式考试基础包和模拟测试基础包
        SysFile basePackage = iSysFileService.getBaseDataByTaskCode(taskCode);
        String basePackageUrl = Optional.ofNullable(basePackage)
                .map(SysFile::getUrl)
                //暂无基础包
                .orElseThrow(() -> new KqException(GCEResultEnum.GET_BASE_PACKAGE_OTHER.getResult(),
                        GCEResultEnum.GET_BASE_PACKAGE_OTHER.getRemark()));
        //拼接返回结果
        StringBuffer result = new StringBuffer();
        result.append(GCEResultEnum.GET_BASE_PACKAGE_CAN_DOWNLOAD.getResult())
                .append(ActionEnum.VERTICAL_SIGN.getName())
                .append(basePackageUrl);
        String encrypString = SecModuleUtil.encrypStringNew(result.toString());
        String decrypString = SecModuleUtil.decrypStringNew(encrypString);
        log.info("基础包数据解密：{}",decrypString);
        log.info("基础包数据长度：{}",result.toString().length());
        int length = SecModuleUtil.encrypStringNew(result.toString()).length();
        log.info("基础包加密后数据长度：{}",length);
        int i = 20;
        while(!decrypString.endsWith("bdp") && i > 0){
            encrypString = encrypString.substring(0,encrypString.length()-1);
            decrypString = SecModuleUtil.decrypStringNew(encrypString);
            log.info(i+"基础包解密密后数据：{}",decrypString);
            i--;
        }
        //加密返回结果
        return encrypString;
    }

    @Override
    public String updateBaseDataState(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //暂无基础包下载管理表，直接返回成功状态
        return String.valueOf(GCEResultEnum.UPDATE_BASE_DATA_STATE_SUCCESS.getResult());
    }

    @Override
    public String getTaskCode(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        return null;
    }

    @Override
    public String getResitRoomPackage(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //考务暂不支持获取重考考务包
        return String.valueOf(GCEResultEnum.GET_RESIT_ROOM_PACKAGE_NOT_FOUND.getResult());
    }

    @Override
    public String updateResitRoomPackageState(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //暂无重考考务包下载管理表，直接返回成功状态
        return String.valueOf(GCEResultEnum.UPDATE_RESIT_ROOM_PACKAGE_STATE_SUCCESS.getResult());
    }

    @Override
    public String checkExamSystemConnection(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        return String.valueOf(GCEResultEnum.CHECK_EXAM_SYSTEM_CONNECTION_SUCCESS.getResult());
    }

    @Override
    public String getServerTime(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        ServerTimeResult serverTimeResult = new ServerTimeResult();
        try {
            serverTimeResult.setTime(String.valueOf(DateTimeUtil.date2double(Calendar.getInstance().getTime())))
                    .setFlag(GCEResultEnum.GET_SERVER_TIME_SUCCESS.getResult());
        } catch (Exception e) {
            //获取服务器时间失败
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "获取服务器时间失败", e);
            serverTimeResult.setFlag(GCEResultEnum.GET_SERVER_TIME_FAILED.getResult());
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    XmlUtil.toXml(serverTimeResult));
        }
        return XmlUtil.toXml(serverTimeResult);
    }

    @Override
    public String getCandidateInfo(HttpServletRequest request, HttpServletResponse response, String taskCode) throws Exception {
        //获取请求参数
        String admissionNumber = request.getParameter(GCEParamEnum.GET_CANDIDATE_INFO_ADMISSION_NUMBER.getParam());
        String placeCode = request.getParameter(GCEParamEnum.GET_CANDIDATE_INFO_PLACE_CODE.getParam());
        String roomCode = request.getParameter(GCEParamEnum.GET_CANDIDATE_INFO_ROOM_CODE.getParam());
        String timeCode = request.getParameter(GCEParamEnum.GET_CANDIDATE_INFO_TIME_CODE.getParam());
        //返回结果
        CandidateInfoGceResult result = new CandidateInfoGceResult();
        //获取考生信息返回结果
        List<CandidateInfoGceResultDTO> candidateInfoGceResultDTOList = iCandidateService
                .getCandidateInfoGceResultDTO(PhotoTypeEnum.PERSONAL.getCode(), taskCode, admissionNumber);
        //校验考生是否存在
        if (CollectionUtils.isEmpty(candidateInfoGceResultDTOList)) {
            //未找到考生信息
            result.setFlag(GCEResultEnum.GET_CANDIDATE_INFO_NOT_FOUND.getResult());
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    GCEResultEnum.GET_CANDIDATE_INFO_NOT_FOUND.getRemark());
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    XmlUtil.toXml(result));
        }
        //校验当前考点编号（注意：不可能存在同一个考生不同批次考试在不同考点的情况）
        boolean placeMatch = candidateInfoGceResultDTOList.stream()
                .allMatch(info -> info.getPlaceCode().equals(placeCode));
        //校验在线添加的考生是否在当前考点
        if (!placeMatch) {
            //考生不是本考点考生，无法转移
            result.setFlag(GCEResultEnum.GET_CANDIDATE_INFO_NOT_CURRENT_PLACE.getResult());
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    GCEResultEnum.GET_CANDIDATE_INFO_NOT_CURRENT_PLACE.getRemark());
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    XmlUtil.toXml(result));
        }
        //获取当前批次考生信息（注意：同一个考生在同一个批次下只可能有一条编排数据）
        CandidateInfoGceResultDTO candidateInfoGceResultDTO = candidateInfoGceResultDTOList.stream()
                //校验是否有当前批次的考生信息
                .filter(info -> info.getTimeCode().equals(timeCode))
                .findFirst()
                //考生不是本批次考生，无法转移
                .orElseThrow(() -> new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                        XmlUtil.toXml(result.setFlag(GCEResultEnum.GET_CANDIDATE_INFO_NOT_CURRENT_TIME.getResult()))));
        //校验考生是否为当前考场的考生
        if (candidateInfoGceResultDTO.getRoomCode().equals(roomCode)) {
            //本考场考生，无需转移
            result.setFlag(GCEResultEnum.GET_CANDIDATE_INFO_IS_CURRENT_ROOM.getResult());
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}",
                    GCEResultEnum.GET_CANDIDATE_INFO_IS_CURRENT_ROOM.getRemark());
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    XmlUtil.toXml(result));
        }
        BeanUtils.copyProperties(candidateInfoGceResultDTO, result);
        if (!StringUtils.isEmpty(result.getPhoto())) {
            //转换考生照片数据
            InputStream inputStream = HuaweiCloudObsUtil.getInputStream(result.getPhoto());
            String encoded = Base64.getEncoder().encodeToString(IOUtils.toByteArray(inputStream));
            inputStream.close();
            result.setPhoto(encoded);
        }
        result.setExamType(String.valueOf(GCEResultEnum.GET_CANDIDATE_INFO_EXAM_TYPE.getResult()))
                .setFlag(GCEResultEnum.GET_CANDIDATE_INFO_SUCCESS.getResult());
        return XmlUtil.toXml(result);
    }

    @Override
    public String getResultUploadPath(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String roomCode = request.getParameter(GCEParamEnum.GET_RESULT_UPLOAD_PATH_ROOM_CODE.getParam());;
        //获取考务地址
        String examinationUrl = configManager.getExaminationUrl();
        StringBuffer result = new StringBuffer();
        if (StringUtils.isBlank(examinationUrl)) {
            //考务地址不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "考务地址不存在");
            throw new KqException(GCEResultEnum.GET_RESULT_UPLOAD_PATH_OTHER.getResult(),
                    GCEResultEnum.GET_RESULT_UPLOAD_PATH_OTHER.getRemark());
        }
        //校验考场是否存在
        if (!iTaskRoomService.existByTaskCodeAndCode(taskCode, roomCode)) {
            //考务地址不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "考场不存在");
            throw new KqException(GCEResultEnum.GET_RESULT_UPLOAD_PATH_OTHER.getResult(),
                    GCEResultEnum.GET_RESULT_UPLOAD_PATH_OTHER.getRemark());
        }
        //组装返回结果
        result.append(GCEResultEnum.GET_RESULT_UPLOAD_PATH_SUCCESS.getResult())
                //竖线分隔符
                .append(ActionEnum.VERTICAL_SIGN.getName())
                //考务地址
                .append(examinationUrl)
                //考试任务编号
                .append(taskCode)
                //斜杠
                .append(GCEUploadConstant.DIRECTORY_SEPARATOR)
                //GCE考试系统上传文件接口名称
                .append(GCEUploadConstant.INTERFACE_NAME)
                //斜杠
                .append(GCEUploadConstant.DIRECTORY_SEPARATOR)
                //上传文件类型：考试结果
                .append(GCEUploadConstant.UPLOAD_TYPE_RESULT)
                //斜杠
                .append(GCEUploadConstant.DIRECTORY_SEPARATOR)
                //考场编号
                .append(roomCode);
        return result.toString();
    }

    @Override
    public String verifyResult(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String path = request.getParameter(GCEParamEnum.VERIFY_RESULT_PATH.getParam());
        //获取考试结果文件所属的场次数据
        Optional<TimeRoom> timeRoomOptional = this.getTimeRoomByPath(taskCode, path);
        //通过考试结果文件所属的场次数据获取最新的考试结果数据
        Boolean success = timeRoomOptional.map(timeRoom -> {
            //获取包类型
            String packageType = path.substring(path.lastIndexOf('.') + 1);
            Optional<ExamResultMonitor> examResultMonitorOptional = iExamResultMonitorService.lambdaQuery()
                    .select(ExamResultMonitor::getSuccess, ExamResultMonitor::getTimestamp)
                    .eq(ExamResultMonitor::getTaskId, timeRoom.getTaskId())
                    .eq(ExamResultMonitor::getTimeId, timeRoom.getTimeId())
                    .eq(ExamResultMonitor::getRoomId, timeRoom.getRoomId())
                    .eq(ExamResultMonitor::getPackageType, packageType)
                    .eq(ExamResultMonitor::getDelFlag, false)
                    .list().stream()
                    .max(Comparator.comparing(ExamResultMonitor::getTimestamp));
            return examResultMonitorOptional.map(ExamResultMonitor::getSuccess)
                    .orElse(false);
        }).orElseThrow(() -> new KqException(GCEResultEnum.VERIFY_RESULT_EXCEPTION.getResult(),
                GCEResultEnum.VERIFY_RESULT_EXCEPTION.getRemark()));
        //获取校验结果
        int result = success ? GCEResultEnum.VERIFY_RESULT_SUCCESS.getResult() : GCEResultEnum.VERIFY_RESULT_FAILED.getResult();
        return String.valueOf(result);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateResultState(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String timeRoomCode = request.getParameter(GCEParamEnum.UPDATE_RESULT_STATE_TIME_ROOM_CODE.getParam());
        String type = request.getParameter(GCEParamEnum.UPDATE_RESULT_STATE_TYPE.getParam());
        //获取任务批次、考场信息
        TimeRoom timeRoom = iTimeRoomService.getByTaskTimeRoomCode(taskCode, timeRoomCode);
        if (ObjectUtils.isEmpty(timeRoom)) {
            //场次信息不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "场次信息不存在");
            throw new KqException(GCEResultEnum.UPDATE_RESULT_STATE_FAILED.getResult(),
                    GCEResultEnum.UPDATE_RESULT_STATE_FAILED.getRemark());
        }
        //根据场次信息获取考试结果数据
        List<ExamResultMonitor> examResultMonitorList = iExamResultMonitorService.lambdaQuery()
                .eq(ExamResultMonitor::getTaskId, timeRoom.getTaskId())
                .eq(ExamResultMonitor::getTimeId, timeRoom.getTimeId())
                .eq(ExamResultMonitor::getRoomId, timeRoom.getRoomId())
                .eq(ExamResultMonitor::getSuccess, true)
                .eq(ExamResultMonitor::getDelFlag, false)
                .list();
        List<ExamResultMonitor> updateExamResultMonitorList = new ArrayList<>();
        //筛选出最新的考试结果主包数据
        examResultMonitorList.stream()
                .filter(examResultMonitorMainPackage -> StringCodeEnum.GCE_CDP.getCode()
                        .equals(examResultMonitorMainPackage.getPackageType()))
                .max(Comparator.comparing(ExamResultMonitor::getTimestamp))
                .map(mainPackage -> {
                    mainPackage.setExamType(Integer.valueOf(type));
                    return mainPackage;
                }).ifPresent(updateExamResultMonitorList::add);
        //筛选出最新的考试结果校验包数据
        examResultMonitorList.stream()
                .filter(examResultMonitorVerifyPackage -> StringCodeEnum.GCE_CKP.getCode()
                        .equals(examResultMonitorVerifyPackage.getPackageType()))
                .max(Comparator.comparing(ExamResultMonitor::getTimestamp))
                .map(verifyPackage -> {
                    verifyPackage.setExamType(Integer.valueOf(type));
                    return verifyPackage;
                }).ifPresent(updateExamResultMonitorList::add);
        //更新考试结果数据
        iExamResultMonitorService.updateBatchById(updateExamResultMonitorList);
        //返回成功结果
        return String.valueOf(GCEResultEnum.UPDATE_RESULT_STATE_SUCCESS.getResult());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateExamMonitor(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取请求参数
        String stateType = request.getParameter(GCEParamEnum.UPDATE_EXAM_MONITOR_STATE_TYPE.getParam());
        String roomCode = request.getParameter(GCEParamEnum.UPDATE_EXAM_MONITOR_ROOM_CODE.getParam());
        String timeCode = request.getParameter(GCEParamEnum.UPDATE_EXAM_MONITOR_TIME_CODE.getParam());
        //获取考试过程监控信息
        ExamMonitor examMonitor = iExamMonitorService.getByTimeAndRoomCode(taskCode, roomCode, timeCode);
        //校验考试过程监控信息
        if (ObjectUtils.isEmpty(examMonitor)) {
            //考试过程监控信息不存在
            log.error(request.getParameter(ActionEnum.ACTION.getName()) + ": {}", "考试过程监控信息不存在");
            throw new KqException(GCEResultEnum.UPDATE_EXAM_MONITOR_FAILED.getResult(),
                    GCEResultEnum.UPDATE_EXAM_MONITOR_FAILED.getRemark());
        }
        switch (GCEStateTypeEnum.getByType(stateType)) {
            //下载考务包
            case ROOM_PACKAGE:
                examMonitor.setDownloadRoomData(true);
                break;
            //下载试卷
            case PAPER:
                examMonitor.setDownloadPaper(true);
                break;
            //下载授权码
            case AUTHORIZATION_CODE:
                examMonitor.setDownloadDecode(true);
                break;
            //考试开始
            case STARTED:
                examMonitor.setExamStarted(true);
                break;
            //考试结束
            case ENDED:
                examMonitor.setExamEnded(true);
                break;
            //考试完成
            case FINISHED:
                examMonitor.setFinish(true);
                break;
            //上传考试结果
            case UPLOADED:
                examMonitor.setUploadResult(true);
                break;
            //导出考试结果
            case EXPORTED:
                examMonitor.setExport(true);
                break;
            default:
                break;
        }
        //更新考试过程监控信息
        iExamMonitorService.updateById(examMonitor);
        return String.valueOf(GCEResultEnum.UPDATE_EXAM_MONITOR_SUCCESS.getResult());
    }

    @Override
    public String getTimeByPlace(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        return null;
    }

    @Override
    public String getTestInventory(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //获取考试任务下的测试项清单数据
        TaskTestInventory taskTestInventory = iTaskTestInventoryService.getByTaskCode(taskCode);
        //组装测试项清单数据
        return Optional.ofNullable(taskTestInventory).map(inventory -> {
            //华为云OBS下载测试项清单数据
            String res = tpsClient.getStringFromHuaweiObs(inventory.getPath(), StandardCharsets.UTF_8.name());
            Result result = JSON.parseObject(res, Result.class);
            if (!result.getSuccess() || ObjectUtils.isEmpty(result.getObj())) {
                log.error(request.getParameter(ActionEnum.ACTION.getName())
                        + ": {}", "华为云OBS下载测试项清单数据失败");
                throw new KqException(GCEResultEnum.GET_TEST_INVENTORY_NOT_FOUND.getResult(),
                        GCEResultEnum.GET_TEST_INVENTORY_NOT_FOUND.getRemark());
            }
            //拼接测试项清单返回结果
            return GCEResultEnum.GET_TEST_INVENTORY_SUCCESS.getResult() +
                    //井号符
                    ActionEnum.POUND_SIGN.getName() +
                    //测试项清单数据
                    result.getObj();
        }).orElseGet(() -> String.valueOf(GCEResultEnum.GET_TEST_INVENTORY_NOT_FOUND.getResult()));
    }

    @Override
    public String updateTestInventoryState(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //暂无模拟测试项清单状态管理表，直接返回成功
        return String.valueOf(GCEResultEnum.UPDATE_TEST_INVENTORY_STATE_SUCCESS.getResult());
    }

    @Override
    public String getHuaweiCloudObsInfo(){
        //获取华为云OBS配置信息
        HuaweiCloudObsInfo obsInfo = new HuaweiCloudObsInfo();
        BeanUtils.copyProperties(huaweiCloudObsConfig, obsInfo);
        String json = JSONObject.toJSONString(obsInfo, SerializerFeature.PrettyFormat,
                SerializerFeature.WriteMapNullValue);
        //加密并返回结果
        return SecModuleUtil.encrypString(json);
    }

    @Override
    public String getOumaExaminationConfig(OumaParam oumaParam) {
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String upload(long timestamp, HttpServletRequest request, HttpServletResponse response,
                         MultipartFile file, String taskCode, String type, String roomCode) {
        //初始化文件上传结果
        UploadResponse uploadResponse = new UploadResponse(GCEResultEnum.UPLOAD_FAILED.getResult(),
                GCEResultEnum.UPLOAD_FAILED_MESSAGE.getRemark());
        try {
            switch (type) {
                //考试结果
                case GCEUploadConstant.UPLOAD_TYPE_RESULT:
                    this.uploadResult(timestamp, request, file, taskCode, uploadResponse);
                    break;
                //监考端配置备份信息
                case GCEUploadConstant.UPLOAD_TYPE_MONITOR_BACKUP:
                    this.uploadMonitorSettingBackup(timestamp, request, file, taskCode, roomCode, uploadResponse);
                    break;
                //暂不支持的考试文件类型
                default:
                    throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                            "暂不支持的考试文件类型: " + type);
            }
        } catch (KqException k) {
            log.error("考试文件包上传业务异常: {}", k.getErrMsg());
            uploadResponse.setCode(GCEResultEnum.UPLOAD_FAILED.getResult())
                    .setMessage(GCEResultEnum.UPLOAD_FAILED_MESSAGE.getRemark());
        } catch (Exception e) {
            log.error("考试文件包上传异常", e);
            uploadResponse.setCode(GCEResultEnum.UPLOAD_FAILED.getResult())
                    .setMessage(GCEResultEnum.UPLOAD_FAILED_MESSAGE.getRemark());
        }
        //文件上传结果序列化为JSON并返回给考试系统
        return JSONObject.toJSONString(uploadResponse);
    }

    /**
     * 上传考试结果
     * @param timestamp 时间戳
     * @param request 请求
     * @param file 考试结果文件
     * @param taskCode 考试任务编号
     * @param uploadResponse 文件上传结果
     */
    private void uploadResult(long timestamp, HttpServletRequest request, MultipartFile file,
                              String taskCode, UploadResponse uploadResponse) {
        //获取请求参数
        String path = request.getParameter(GCEParamEnum.UPLOAD_DIR.getParam());
        String paramMd5 = request.getParameter(GCEParamEnum.UPLOAD_MD5.getParam());
        //获取考试结果文件上传路径并拼接文件名
        path = GCEUploadConstant.getResultPath(taskCode, path) + file.getOriginalFilename();
        //文件路径拼接时间戳
        StringBuilder stringBuilder = new StringBuilder(path);
        int index = stringBuilder.lastIndexOf(".");
        stringBuilder.insert(index, "_" + timestamp);
        path = stringBuilder.toString();
        HuaweiObsObjectMetaDataVO huaweiObsObjectMetaDataVO = null;
        try {
            //校验考试结果文件包
            this.uploadVerify(file, paramMd5);
            //调用tps接口上传文件至华为云OBS
            try (InputStream inputStream = file.getInputStream()) {
                huaweiObsObjectMetaDataVO = this.uploadToHuaweiObs(path, inputStream, paramMd5);
            }
            //MD5校验通过、文件上传成功，才表示成功
            uploadResponse.setCode(GCEResultEnum.UPLOAD_SUCCESS.getResult())
                    .setMessage(GCEResultEnum.UPLOAD_SUCCESS_MESSAGE.getRemark());
        } catch (KqException k) {
            log.error("考试结果上传业务异常: {}", k.getErrMsg());
        } catch (Exception e) {
            log.error("考试结果上传异常", e);
        }
        //保存考试结果上传记录（无论上传成功与否，都要保存上传记录）
        this.saveResultMonitor(taskCode, path, uploadResponse,
                huaweiObsObjectMetaDataVO, timestamp);
    }

    /**
     * 上传监考端配置备份信息
     * @param timestamp 时间戳
     * @param request 请求
     * @param file 考试结果文件
     * @param taskCode 考试任务编号
     * @param roomCode 考场编号
     * @param uploadResponse 文件上传结果
     */
    private void uploadMonitorSettingBackup(long timestamp, HttpServletRequest request, MultipartFile file,
                                            String taskCode, String roomCode, UploadResponse uploadResponse) {
        //获取请求参数
        String paramMd5 = request.getParameter(GCEParamEnum.UPLOAD_MD5.getParam());
        //获取考试文件上传路径
        String path = GCEUploadConstant.getMonitorSettingBackupPath(taskCode, roomCode);
        //文件路径拼接时间戳
        path = path + GCEUploadConstant.DIRECTORY_SEPARATOR + file.getOriginalFilename() + "_" + timestamp;
        HuaweiObsObjectMetaDataVO huaweiObsObjectMetaDataVO = null;
        try {
            //校验考试文件包
            this.uploadVerify(file, paramMd5);
            //调用tps接口上传文件至华为云OBS
            try (InputStream inputStream = file.getInputStream()) {
                huaweiObsObjectMetaDataVO = this.uploadToHuaweiObs(path, inputStream, paramMd5);
            }
            //MD5校验通过，文件上传成功，才表示成功
            uploadResponse.setCode(GCEResultEnum.UPLOAD_SUCCESS.getResult())
                    .setMessage(GCEResultEnum.UPLOAD_SUCCESS_MESSAGE.getRemark());
        } catch (KqException k) {
            log.error("监考端配置备份信息上传业务异常: {}", k.getErrMsg());
        } catch (Exception e) {
            log.error("监考端配置备份信息上传异常", e);
        }
        //保存考端配置备份信息上传记录（只有上传成功了才保存上传记录）
        if (GCEResultEnum.UPLOAD_SUCCESS.getResult() == uploadResponse.getCode()) {
            this.saveMonitorSettingBackup(taskCode, roomCode, path,
                    huaweiObsObjectMetaDataVO, timestamp);
        }
    }

    /**
     * 校验考试文件包
     * @param file 文件
     * @param paramMd5 文件MD5
     */
    private void uploadVerify(MultipartFile file, String paramMd5) throws Exception {
        //校验文件MD5
        String md5 = FileUtil.getFileMd5(file);
        if (!md5.equalsIgnoreCase(paramMd5)) {
            //MD5校验不通过
            log.error("上传的文件MD5（{}），与校验后的文件MD5（{}）不一致",
                    paramMd5.toLowerCase(), md5.toLowerCase());
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    "上传的文件MD5与校验后的文件MD5不一致");
        }
    }

    /**
     * 调用TPS接口上传文件至华为云OBS
     * @param path 考试文件上传路径
     * @param inputStream 文件输入流
     * @param paramMd5 文件MD5
     * @throws Exception 异常
     */
    private HuaweiObsObjectMetaDataVO uploadToHuaweiObs(String path, InputStream inputStream, String paramMd5) throws Exception {
        //上传至华为云OBS
        try{
            String resultString = cloudObsService.updateFile(path, inputStream);
            /*
            ApiResponse<HuaweiObsObjectMetaDataVO> objectMetadataResponse = tpsClient.getHuaweiObsObjectMetaData("", path);
            if (objectMetadataResponse.getCode() != 1) {
                throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                        "获取文件在华为云OBS上的MD5失败");
            }*/
            //获取文件在华为云OBS上的MD5
            HuaweiObsObjectMetaDataVO huaweiObsObjectMetaDataVO = new HuaweiObsObjectMetaDataVO();
            /*
            String huaweiObsMd5 = huaweiObsObjectMetaDataVO.getEtag();
            if (!paramMd5.equalsIgnoreCase(huaweiObsMd5)) {
                log.error("华为云OBS上的文件MD5（{}）与上传的文件MD5（{}）不一致",
                        huaweiObsMd5.toLowerCase(), paramMd5.toLowerCase());
                throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                        "华为云OBS上的文件MD5与上传的文件MD5不一致");
            }*/
            huaweiObsObjectMetaDataVO.setUrl(resultString);
            return huaweiObsObjectMetaDataVO;
        } catch (Exception ex){
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    "考试文件包上传至华为云OBS失败");
        }
    }

    /**
     * 保存考试结果上传记录
     * @param taskCode 考试任务编号
     * @param path 考试结果路径
     * @param uploadResponse 上传结果对象
     * @param huaweiObsObjectMetaDataVO 华为云OBS对象属性
     */
    private void saveResultMonitor(String taskCode, String path, UploadResponse uploadResponse,
                                   HuaweiObsObjectMetaDataVO huaweiObsObjectMetaDataVO, long timestamp) {
        //考试结果文件所属的场次数据
        Optional<TimeRoom> optional = this.getTimeRoomByPath(taskCode, path);
        ExamResultMonitor resultMonitor = optional.map(timeRoom -> {
            ExamResultMonitor examResultMonitor = new ExamResultMonitor();
            examResultMonitor.setTaskId(timeRoom.getTaskId())
                    .setTimeId(timeRoom.getTimeId())
                    .setRoomId(timeRoom.getRoomId())
                    .setPackageType(path.substring(path.lastIndexOf('.') + 1))
                    .setPackageName(path.substring(path.lastIndexOf("/") + 1))
                    .setPath(path)
                    .setSize(ObjectUtils.isEmpty(huaweiObsObjectMetaDataVO) ? null : huaweiObsObjectMetaDataVO.getContentLength())
                    .setTimestamp(timestamp)
                    .setSuccess(GCEResultEnum.UPLOAD_SUCCESS.getResult() == uploadResponse.getCode());
            UpdateWrapper<ExamResultMonitor> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(ExamResultMonitor::getDelFlag,true).eq(ExamResultMonitor::getTimeId,timeRoom.getTimeId())
                    .eq(ExamResultMonitor::getRoomId,timeRoom.getRoomId()).eq(ExamResultMonitor::getTaskId,timeRoom.getTaskId()).eq(ExamResultMonitor::getPackageType,path.substring(path.lastIndexOf('.') + 1));
            iExamResultMonitorService.update(updateWrapper);
            return examResultMonitor;
        }).orElseThrow(() -> new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                "获取文件所属的考场、批次失败"));

        //插入数据库
        if (!iExamResultMonitorService.save(resultMonitor)) {
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    "插入考试结果上传监控表失败");
        }
    }

    /**
     * 保存监考机配置备份信息上传记录
     * @param taskCode 考试任务编号
     * @param roomCode 考场编号
     * @param path 监考机配置备份信息路径
     * @param huaweiObsObjectMetaDataVO 华为云对象属性VO
     * @param timestamp 时间戳
     */
    private void saveMonitorSettingBackup(String taskCode, String roomCode, String path,
                                          HuaweiObsObjectMetaDataVO huaweiObsObjectMetaDataVO, long timestamp) {
        //获取监考机配置备份信息文件所属的考场信息
        TaskRoom taskRoom = iTaskRoomService.getByTaskCodeAndCode(taskCode, roomCode);
        //插入监考机配置备份信息表
        MonitorSettingBackup monitorSettingBackup = new MonitorSettingBackup();
        monitorSettingBackup.setTaskId(taskRoom.getTaskId())
                .setRoomId(taskRoom.getId())
                .setPath(path)
                .setUrl(ObjectUtils.isEmpty(huaweiObsObjectMetaDataVO) ? null : huaweiObsObjectMetaDataVO.getUrl())
                .setTimestamp(timestamp);
        if (!iMonitorSettingBackupService.save(monitorSettingBackup)) {
            throw new KqException(ResponseCode.EXAMINATION_EXAM_SYSTEM_BUSINESS_EXCEPTION.getCode(),
                    "插入监考机配置备份信息表失败");
        }
    }

    /**
     * 获取考试结果文件所属的场次数据
     * @param taskCode 考试任务编号
     * @param path 考试结果路径
     * @return 考试结果文件所属的场次数据
     */
    private Optional<TimeRoom> getTimeRoomByPath(String taskCode, String path) {
        //获取文件所属的考场、批次
        String name = path.substring(path.lastIndexOf("/") + 1);
        String[] str = name.split("_");
        String roomCode = str[1];
        String timeCode = str[2];
        return iTimeRoomService.getByTaskTimeRoomCode(taskCode, timeCode, roomCode);
    }

    @Override
    public String registerRoom(HttpServletRequest request, HttpServletResponse response, String taskCode) {
        //OBT系统的注册接口，暂不适用于GCE
        return null;
    }

}
